package com.azdebugit.trancation.test;

import com.azdebugit.trancation.test.enu.HandleMethodEnum;
import com.azdebugit.trancation.test.enu.StatusEnum;
import com.azdebugit.trancation.test.inter.Compensate;
import com.azdebugit.trancation.test.pojo.CompensationEntity;
import com.azdebugit.trancation.test.pojo.MethodInfo;
import com.azdebugit.trancation.test.service.CompensationService;
import com.azdebugit.trancation.test.util.ValidateUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.base.Throwables;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;

//切面类
@Aspect
@Component
public class AnnotationCompensateEntry {
    private static final Logger logger = LoggerFactory.getLogger(AnnotationCompensateEntry.class);
    @Autowired
    private CompensationService compensationService;

    /**
     * 切点，通过注解的方式
     *
     * @param joinPoint 连接点
     * @param e         异常
     */
    @AfterThrowing(pointcut = "@annotation( com.azdebugit.trancation.test.inter.Compensate)", throwing = "e")
    public void aopCompensate(JoinPoint joinPoint, Exception e) {
        try {
            // 1：获取需要的数据，并且进行验证
            Method targetMethod = ((MethodSignature) joinPoint.getSignature()).getMethod();
            Compensate compensate = targetMethod.getAnnotation(Compensate.class);
            Object[] args = joinPoint.getArgs();
            if (args.length == 0) {
                logger.info("无参方法无法补偿！");
                return;
            }
            //异常类型
            Class<?>[] exceptionTypes = compensate.exceptionType();
            for (Class<?> exceptionType : exceptionTypes) {
                // exceptionType 是 e.getClass() 的父类或者一样的。
                if (exceptionType.isAssignableFrom(e.getClass())) {
                    saveCompensationDO(e, joinPoint);
                }
            }
        } catch (Throwable throwable) {
            // 异常需要抛出。要不然没办法，业务代码依赖这个报错的信息会被干扰。
            logger.error(throwable.getMessage(), throwable);
            Throwables.propagateIfPossible(throwable);
        }
    }

    /**
     * 2： 保存异常数据
     * 保存的数据，应该先查询下，看数据库中是否存在这样的数据，如果不存在就进行插入。存在啥也不做。
     *
     * @param e         异常类
     * @param joinPoint 切面类
     */
    public void saveCompensationDO(Exception e, JoinPoint joinPoint) throws JsonProcessingException {
        // 1：获取切面对象的参数
        MethodInfo methodInfo = new MethodInfo(joinPoint);
        methodInfo.init();
        // 2：构建需要补偿的对象
        CompensationEntity compensationDO = buildCompensationDO(methodInfo, e);
        // 3: 上面根据的数据，得保证是满足数据的完整性。然后进行数据的储存入库。然后结束。
        List<CompensationEntity> compensationEntities = compensationService.findByProperty(CompensationEntity.class, "busKey", compensationDO.getBusKey());
        if (CollectionUtils.isEmpty(compensationEntities)) {
            compensationService.save(compensationDO);
        }
    }

    /**
     * 2-1: 数据转换
     *
     * @param methodInfo 包装过后的数据对象
     * @return 转换的补偿类
     */
    private CompensationEntity buildCompensationDO(MethodInfo methodInfo, Exception e) {
        // 1：存入通用的参数
        CompensationEntity compensationDO = new CompensationEntity();
        Annotation[] annotations= methodInfo.getTargetClass().getAnnotations();
        String springBeanName="";
        for (Annotation annotation : annotations) {
            if (annotation.annotationType().equals(Service.class)){
                springBeanName=methodInfo.getTargetClass().getAnnotation(Service.class).value();
            }
            if (annotation.annotationType().equals(Component.class)){
                springBeanName=methodInfo.getTargetClass().getAnnotation(Component.class).value();
            }
        }
        if (StringUtils.isEmpty(springBeanName)){
            springBeanName=methodInfo.getTargetClass().getSimpleName();
        }
        compensationDO.setBeanName(springBeanName);
        compensationDO.setClassName(methodInfo.getClassName());
        compensationDO.setMethodName(methodInfo.getMethodName());
        compensationDO.setDataStatus(StatusEnum.StatusEnum_INIT.getCode());
        compensationDO.setRetryCount(0);
        compensationDO.setResultMsg(e.getMessage());
        compensationDO.setType(HandleMethodEnum.AUTO.getMsg());
        // 2：处理请求参数类型
        // 一：消息无转换，说明入参是一样的。直接存。 1: 单个->类型 2：多个->数组 3. 无参不处理
        Object[] args = methodInfo.getArgs();
        ValidateUtils.isFalse(args.length == 0, "无参方法不允许使用补偿注解！");
        if (args.length == 1) {
            //获取方法参数类型数组
            compensationDO.setReqArgs(methodInfo.getJsonArgs());
            compensationDO.setReqArgsType(args[0].getClass().getName());
        }
        if (args.length > 1) {
            Class<?>[] classesReq = methodInfo.getMethod().getParameterTypes();
            StringBuilder stringBuffer = new StringBuilder();
            for (Class<?> aClass : classesReq) {
                stringBuffer.append(aClass.getName())/*.append(CommonConstants.Public.COMMA)*/;
            }
            compensationDO.setReqArgs(methodInfo.getJsonArgs());
            compensationDO.setReqArgsType(Object[].class.getName());
            compensationDO.setReqArgsTypeReal(stringBuffer.substring(0, stringBuffer.length() - 1));
        }
        compensationDO.setBusKey(compensationDO.createBusKey());
        return compensationDO;
    }
}